package com.jn.retrofit.http;

import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;

import androidx.annotation.NonNull;

import com.jn.retrofit.common.WeakRrfHandler;
import com.jn.retrofit.utils.GsonUtils;
import com.jn.retrofit.utils.LogUtils;

import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class HttpHelper {
    private static final int MSG_SUCCESS = 0;
    private static final int MSG_FAIL = 1;
    private static final int MSG_TOKEN = 2;
    private static final int MSG_ERROR = 3;
    private static final int MSG_TIMEOUT = 4;
    private final OkHttpClient mClient;
    private static HttpHelper instance;
    private final Map<String, HttpCallback> mCallbackMap;

    private HttpHelper() {
        mClient = new OkHttpClient().newBuilder()
                .addInterceptor(new HeaderInterceptor())
                .addInterceptor(new BodyInterceptor())
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)
                .build();
        mCallbackMap = new HashMap<>();
    }

    public static HttpHelper getInstance() {
        if (instance == null) {
            instance = new HttpHelper();
        }
        return instance;
    }

    public void postRequest(HttpRequest request) {
        addCallback(request.url, request.callBack);
        post(request.paramsMap, request.headersMap, request.url);
    }

    private void addCallback(String url, HttpCallback callBack) {
        if (mCallbackMap != null) {
            mCallbackMap.put(url, callBack);
        }
    }

    public void deleteCallback(String url) {
        if (mCallbackMap != null && mCallbackMap.containsKey(url)) {
            mCallbackMap.remove(url);
        }
    }

    private void post(Map<String, String> paramsMap, Map<String, String> headersMap, String url) {
        FormBody.Builder builder = new FormBody.Builder();
        for (String key : paramsMap.keySet()) {
            builder.add(key, paramsMap.get(key));
        }
        FormBody formBody = builder.build();

        LogUtils.d("asdf", "url = " + url);
        Request.Builder requestbuilder = new Request.Builder();

        for (String key : headersMap.keySet()) {
            requestbuilder.addHeader(key, headersMap.get(key));
        }
        Request request = requestbuilder.url(HttpContract.HOST + url).post(formBody).build();

        Call call = mClient.newCall(request);

        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                if (e instanceof SocketTimeoutException) {
                    LogUtils.d("asdf", "-----------------timeout-------------------------");
                    fail(call, MSG_TIMEOUT);
                } else {
                    fail(call, MSG_FAIL);
                }
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                response(call, response);
            }
        });
    }

    private void fail(Call call, int what) {
        String url = call.request().url().toString();
        LogUtils.d("asdf", "onFailure and url is " + url);
        Message xiaoxi = Message.obtain();
        xiaoxi.what = what;
        xiaoxi.obj = url.substring(HttpContract.HOST.length());
        mHandler.sendMessage(xiaoxi);
    }

    //根据实际项目情况解析，各个项目的返回码可能不一致
    private void response(Call call, Response response) {
        try {
            String body = response.body().string();
            String code = GsonUtils.getKeyRecursively(body, "error_code");
            LogUtils.d("asdf", "code === " + code);
            String url = call.request().url().toString();
            if (code.contains("405")) {
                LogUtils.d("asdf", "token, url is " + url + ", \n body is " + body);
                Message msg = Message.obtain();
                String[] data = new String[]{body, url.substring(HttpContract.HOST.length())};
                msg.what = MSG_TOKEN;
                msg.obj = data;
                mHandler.sendMessage(msg);
            } else if (code.equals("200") || code.equals("0")) {
                LogUtils.d("asdf", "success, and url is " + url);
                Message succMsg = Message.obtain();
                String[] data = new String[]{body, url.substring(HttpContract.HOST.length())};
                succMsg.what = MSG_SUCCESS;
                succMsg.obj = data;
                mHandler.sendMessage(succMsg);
            } else {
                LogUtils.d("asdf", "error, and url is " + url + "\n body : " + body);
                String errorMsg = GsonUtils.getKeyRecursively(body, "msg");
                if (TextUtils.isEmpty(errorMsg)) {
                    errorMsg = GsonUtils.getKeyRecursively(body, "error");
                }
                Message msg = Message.obtain();
                String[] data = new String[]{errorMsg, url.substring(HttpContract.HOST.length()), code.replace(".0", "")};
                msg.what = MSG_ERROR;
                msg.obj = data;
                mHandler.sendMessage(msg);
            }
        } catch (IOException e) {

        }
    }

    WeakRrfHandler<HttpHelper> mHandler = new WeakRrfHandler<>(Looper.getMainLooper(), this, new android.os.Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            int waote = msg.what;

            switch (waote) {
                case MSG_SUCCESS:
                    String[] data = (String[]) msg.obj;
                    if (data == null || data.length < 2) {
                        return false;
                    }
                    String succ_url = data[1];
                    HttpCallback succ_callback = mCallbackMap.get(succ_url);
                    if (succ_callback != null) {
                        succ_callback.onHttpSuccess(data[0], succ_url);
                    }
                    deleteCallback(succ_url);
                    break;
                case MSG_FAIL:
                    String fail_url = (String) msg.obj;
                    HttpCallback fail_callback = mCallbackMap.get(fail_url);
                    if (fail_callback != null) {
                        fail_callback.onHttpFail(fail_url);
                    }
                    deleteCallback(fail_url);
                    break;
                case MSG_TIMEOUT:
                    String timeout_url = (String) msg.obj;
                    HttpCallback timeout_callback = mCallbackMap.get(timeout_url);
                    if (timeout_callback != null) {
                        timeout_callback.onHttpTimeout(timeout_url);
                    }
                    deleteCallback(timeout_url);
                    break;
                case MSG_TOKEN:
                    String[] tokenData = (String[]) msg.obj;
                    if (tokenData == null || tokenData.length < 2) {
                        return false;
                    }
                    String token_url = tokenData[1];
                    String token_msg = tokenData[0];
                    HttpCallback token_callback = mCallbackMap.get(token_url);
                    if (token_callback != null) {
                        token_callback.onHttpTokenExpiration(token_msg, token_url);
                    }
                    deleteCallback(token_url);
                    break;
                case MSG_ERROR:
                    String[] errorData = (String[]) msg.obj;
                    if (errorData == null || errorData.length < 3) {
                        return false;
                    }
                    String err_url = errorData[1];
                    String err_msg = errorData[0];
                    String err_code = errorData[2];
                    HttpCallback err_cbk = mCallbackMap.get(err_url);
                    if (err_cbk != null) {
                        err_cbk.onHttpError(err_msg, err_url, err_code);
                    }
                    deleteCallback(err_url);
                    break;
            }
            return false;
        }
    });

    /**
     * 拦截器，添加公共请求头
     */
    static class HeaderInterceptor implements Interceptor {

        @Override
        public Response intercept(Chain chain) throws IOException {
            Request oldRequest = chain.request();
            Request newRequest;
            newRequest = oldRequest.newBuilder()
                    .addHeader("sadCommunismFancyThing", "301")
                    .addHeader("livelyTypewriterNorthInternationalDormitory", "googleplay")
                    .addHeader("hillyViolentToothacheAloneMommy", "1.0.0")
                    .addHeader("differentMaximumPostman", "1")
                    .addHeader("giftedLastRoofPlanet", "6123545623254512358")
                    .addHeader("untrueNuclearTeam", "6123545623254512358")
                    .addHeader("allAtomDelightedGarbage", "1")
                    .addHeader("unpleasantHunterUnknownJam", "true")
                    .addHeader("earlyInventionDollCent", "6123545623254512358")
                    .build();
            return chain.proceed(newRequest);
        }
    }

    /**
     * 拦截器，添加公共参数
     */
    static class BodyInterceptor implements Interceptor {

        @Override
        public Response intercept(Chain chain) throws IOException {
            Request oldRequest = chain.request();
            Request.Builder newRequestBuilder = oldRequest.newBuilder();
            RequestBody body = oldRequest.body();
            if (body == null) {
                LogUtils.d("asdf", "body is null");
            } else if (body instanceof FormBody) {
                LogUtils.d("asdf", "body is FormBody");
                FormBody oldBody = (FormBody) body;
                FormBody.Builder newFormBodyBuilder = new FormBody.Builder();
                for (int i = 0; i < oldBody.size(); i++) {
                    newFormBodyBuilder.add(oldBody.name(i), oldBody.value(i));
                }

                //添加公共参数
                newFormBodyBuilder.add("magicPhotoChocolate", "301");
                newFormBodyBuilder.add("rareToothbrushChimneyLocalAmbulance", "60,120");
                newFormBodyBuilder.add("lessPerfectGeneration", "es");
                newFormBodyBuilder.add("hillyViolentToothacheAloneMommy", "1.0.0");
                newFormBodyBuilder.add("differentMaximumPostman", "1");
                newFormBodyBuilder.add("earlyInventionDollCent", "6123545623254512358");
                newFormBodyBuilder.add("untrueNuclearTeam", "6123545623254512358");
                newFormBodyBuilder.add("healthyLongReceipt", "10.21.33.44");
                newFormBodyBuilder.add("livelyTypewriterNorthInternationalDormitory", "googleplay");
                newFormBodyBuilder.add("greekTortoiseFestivalJuneMadDegree", "NEO4");
                newFormBodyBuilder.add("bigThought", "7777788888");
                newFormBodyBuilder.add("nervousRole", "123456789123456789123456789123456789");
                newFormBodyBuilder.add("busyPortThankfulTemperature", "3");

                FormBody newFormBody = newFormBodyBuilder.build();
                newRequestBuilder.post(newFormBody);
            } else {
                LogUtils.d("asdf", "body is others");
            }

            return chain.proceed(newRequestBuilder.build());
        }
    }
}
